Setup

knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)
library(tidyverse)     # for data cleaning and plotting
library(lubridate)     # for date manipulation
library(palmerpenguins)# for Palmer penguin data
library(dplyr)
# Lisa's garden data
# Palmer penguins
data("penguins")

# US tuition by state data
us_avg_tuition <- read_csv("https://www.dropbox.com/s/9e0paradcwvuzll/us_avg_tuition.csv?dl=1") %>% 
   mutate(across(starts_with("20"), parse_number))

Metas de aprendizagem

Após este tutorial, você deve ser capaz de fazer o seguinte:

  • Use pivot_longer() e pivot_wider() para alterar a forma como os dados são dispostos.
  • Junte tabelas de dados usando as funções de junção dplyr e entenda as diferenças entre os diferentes tipos de junções.
  • Use várias funções forcats, incluindo aquelas não abordadas no tutorial, para alterar a ordem ou os valores dos níveis das variáveis categóricas.
  • Use as funções stringr abordadas neste tutorial (além de separate()) e saiba onde encontrar informações sobre outras funções stringr (DICA: a folha de cheatsheet é um ótimo começo).

Alterar o layout de dados com funções dinâmicas

Esta parte do tutorial irá apresentá-lo a duas funções: pivot_longer () e pivot_wider (). Essas funções são usadas para alterar a forma como os dados são dispostos. O GIF abaixo ilustra o que as funções fazem. Eu o encorajo a revisitar esta ilustração depois de ler as descrições mais detalhadas.

Image credit: Mara Averick (tweet from 2019-10-04)

pivot_longer()

** pivot_longer () **: torna o conjunto de dados mais longo, reduzindo o número de colunas e aumentando o número de linhas. Freqüentemente usado quando os nomes das colunas devem ser valores de uma variável.

Os dados abaixo mostram os custos médios das mensalidades universitárias nos EUA por estado. Observe que os anos são nomes de colunas.

Agora, gostaríamos de mudar isso para que haja uma variável chamada ano que indicaria o ano e os valores da aula seriam uma variável única ao invés de espalhar por várias variáveis -pivot_longer ()para o resgate!

O código genérico para pivot_longer () é mostrado aqui:

data %>% 
  pivot_longer(cols = ___________,
               names_to = "name_of_cols_variable",
               values_to = "name_of_values_variable")

Vamos tentar esse com os dados us_avg_tuition:

us_avg_tuition %>% 
  pivot_longer(cols = starts_with("20"),
               names_to = "year",
               values_to = "avg_tuition")

Agora há uma linha para cada combinação única de estado e ano e ano eavg_tuition são variáveis. Este conjunto de dados tem mais linhas e menos colunas do que o conjunto de dados original.

Vamos examinar cada argumento da função com mais detalhes.

O argumento cols indica quais colunas devem ser dinamizadas para que esses nomes de coluna se tornem valores de uma nova variável. Você pode fazer uma lista de nomes de colunas ou usar funções auxiliares para selecionar colunas (veja a função select () no cheatsheet dplyr para mais detalhes ou procure portidy-select na aba Help). Eu usei a função auxiliar starts_with () neste exemplo.

O argumento names_to é como você deseja nomear a nova variável onde os nomes das colunas serão armazenados. Isso precisa estar entre aspas.

The values_to argument is what you want to name the new variable where the values that used to be spread across multiple columns will now be stored in one variable.

pivot_wider()

pivot_wider (): torna o conjunto de dados mais amplo, reduzindo o número de linhas e aumentando o número de colunas. Freqüentemente usado quando as observações são distribuídas em várias linhas e os valores de uma variável devem ser realmente suas próprias variáveis.

Aqui está um exemplo em que os valores da terceira coluna (Taxa de crescimento anual da população (porcentagem), Taxa de fecundidade total (filhos por mulher), etc.) devem ser cada um sua própria variável.

Agora, vamos ver um exemplo semelhante. Eu criei um novo dataset chamado penguins_fake que é uma reorganização dos dados penguins.

penguins_fake

Em penguins_fake, a coluna chamadamedição possui todos os nomes das medições. Gostaríamos de devolvê-los aos nomes das colunas para que haja novamente apenas uma linha para cada pinguim. Usaremos pivot_wider () para fazer isso! Observe que também há uma coluna chamada obs que identifica cada observação única dos dados originais - isso é muito importante!

O código genérico para pivot_wider () é mostrado aqui:

data %>% 
  pivot_wider(id_cols = ___________,
              names_from = variable_with_names,
              values_from = variable_with_values)

Vamos fazer isso com os dados penguins_fake:

penguins_fake %>% 
  pivot_wider(id_cols = species:obs,
              names_from = measurement,
              values_from = value)

Agora, as quatro variáveis de medição têm, cada uma, sua própria coluna novamente. Este conjunto de dados tem mais colunas e menos linhas do que o conjunto de dados penguins_fake.

Vamos examinar cada argumento da função com mais detalhes.

O argumento id_cols é o conjunto de colunas que identifica exclusivamente cada observação. Por padrão, serão todas as colunas que não estão nos argumentos names_from evalues_from. Como cols da funçãopivot_longer (), você pode fazer uma lista de nomes de coluna ou usar funções auxiliares para selecionar colunas (veja a função select ()na folha de comandodplyr para mais detalhes ou procure por tidy-select na guia Help).

** !!CUIDADO!!: ** É fácil cometer um erro no argumento id_cols. Por exemplo, no código abaixo, esqueci de incluir obs. O resultado é algo estranho e inesperado com apenas 35 linhas.

penguins_fake %>% 
  pivot_wider(id_cols = species:year,
              names_from = measurement,
              values_from = value)

O argumento names_from é a variável (ou variáveis) que contém os valores que você deseja transformar em suas próprias colunas. Isso não está entre aspas.

O argumento values_from é a variável (ou variáveis) que devem ser os valores das novas variáveis.

Video de Apresentação

Agora que você aprendeu o básico sobre pivotar, assista ao vídeo abaixo que o guiará por alguns exemplos de codificação e baixe os arquivos R Markdown para acompanhar. Este é o mesmo arquivo que você usará para os outros tópicos.

Resources

Sua vez!

Exercicio 1: pivot_wider()

Resuma os dados colheita_do_jardim para encontrar o peso total da colheita em libras para cada vegetal e dia da semana. Exiba os resultados de forma que os vegetais sejam linhas, mas os dias da semana sejam colunas.

Exercise 2: pivot_longer()

Use o conjunto de dados billboard (procure na ajuda ou digite? Billboard no console). Tem classificações de músicas para cada semana que entraram no Top 100 da Billboard. As semanas são nomes de colunas. Use pivot_longer () para transformar semanas em uma única coluna e remova linhas com valores ausentes para classificação (DICA: use o argumento values_drop_na empivot_longer ()).

Joining datasets

Ao analisar dados, é comum a necessidade de combinar conjuntos de dados relacionados. Os verbos join nos darão uma maneira de fazer isso. Para todas as junções, devemos estabelecer uma correspondência ou correspondência entre cada caso na tabela da esquerda e zero ou mais casos na tabela da direita.

Uma correspondência entre um caso na tabela left e um caso na tabela right é feita com base nos valores em pares de variáveis correspondentes.

  • Você especifica quais pares usar.
  • Um par é uma variável da tabela da esquerda e uma variável da tabela da direita ou um conjunto de variáveis da tabela da esquerda e da direita.
  • Os casos devem ter valores exatamente iguais no par para que uma correspondência seja feita.

Quando juntamos conjuntos de dados, o formato geral é

left_dataset %>% 
  <JOIN>(right_dataset, 
         by=<HOW TO JOIN>)

onde left_dataset eright_dataset são datasets, <JOIN> é o tipo específico de junção, e <HOW TO JOIN> dá informações detalhadas sobre como fazê-lo.

O argumento by informa como juntar os dois conjuntos de dados, especificamente com quais variáveis ele deve corresponder. Se as variáveis estiverem os mesmos nomes, só precisamos escrever o nome dessa variável, entre aspas: by =" variable_name ".

Se as duas variáveis a serem correspondidas têm nomes diferentes nos dois conjuntos de dados, podemos escrever by = c (" nome1 "=" nome2 "), onde nome1 é a variável no conjunto de dados esquerdo a ser correspondido aonome2variável no conjunto de dados correto.

Também podemos corresponder em várias variáveis usando by = c (" nome1 "=" nome2 "," nome1a "=" nome2a "), onde os nomes à esquerda dos iguais são variáveis do conjunto de dados esquerdo e aqueles no à direita dos iguais são do conjunto de dados certo.

Se by = for omitido de uma junção, então R realizará uma * junção natural *, que combina os dois conjuntos de dados por todas as variáveis que eles têm em comum. É uma boa prática sempre incluir by =.

Vamos discutir os diferentes tipos de junções.

Mutating joins

A primeira classe de junções são junções mutantes, que adicionam novas variáveis (colunas) à tabela de dados esquerda a partir de observações correspondentes na tabela direita.

A principal diferença nas três opções de junção mutante nesta classe é como elas respondem às seguintes perguntas:

  1. O que acontece quando um caso na tabela da direita não tem correspondências na tabela da esquerda?
  2. O que acontece quando um caso na tabela da esquerda não tem correspondências na tabela da direita?

Três funções de junção mutantes:

** left_join () : a saída tem todos os casos da esquerda, independentemente se houver uma correspondência na direita, mas descarta quaisquer casos na direita que não tenham uma correspondência na esquerda. (Há também uma função right_join () ** que faz o oposto.)

Credit: Garrick Aden-Buie – @grrrck

inner_join(): the output has only the cases from the left with a match in the right.

Credit: Garrick Aden-Buie – @grrrck

full_join():a saída tem todos os casos da esquerda e da direita. Isso é menos comum do que os dois primeiros operadores de associação.

Credit: Garrick Aden-Buie – @grrrck

Quando há várias correspondências na tabela da direita para um caso específico na tabela da esquerda, todos os três desses operadores de junção mutante produzem um caso separado na nova tabela para cada uma das correspondências da direita.

Examples

Primeiro, crie dois pequenos conjuntos de dados:

general_info <- tibble(
  person_id = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
  age = c(34, 54, 67, 92, 21, 32, 18, 45, 34, 55),
  rent_or_own = c("rent", "rent", "own", "rent", "rent", "own", "rent", "own", "own", "own")
)

general_info
pet_info <- tibble(
  person_id = c(2,3,5,7,8,10,11,12,13,14,15),
  pet_owner = c("yes", "no", "no", "yes", "yes", "no", "no", "no", "yes", "no", "no")
)

pet_info
  1. Comece com general_info e left_join() o pet_info by person_id:
general_info %>% 
  left_join(pet_info, 
            by = "person_id")

A tabela resultante tem 10 linhas de dados - as 10 observações de general_info. Existem valores ausentes para pet_owner paraperson_id que estavam na tabela general_info e não na tabelapet_info.

** ??? ** Como os resultados mudariam se right_join () fosse usado no código acima em vez de left_join ()?

  1. Comece com general_info einner_join ()opet_info por person_id:
general_info %>% 
  inner_join(pet_info, 
             by = "person_id")

A tabela resultante tem apenas 6 linhas com as observações que estão em general_info epet_info.

  1. Comece com general_info efull_join ()opet_info por person_id:
general_info %>% 
  full_join(pet_info, 
            by = "person_id")

A tabela resultante possui 15 linhas. Estão faltando valores para pet_owner paraperson_id que estavam na tabela general_info e não na tabelapet_info, e estão faltando valores para idade ealuguel para person_id que estavam na tabela pet_info e não na tabelageneral_info.

Filtering joins

A segunda classe de junções são junções de filtragem, que selecionam casos específicos da tabela da esquerda com base em se eles correspondem a uma observação na tabela da direita.

** semi_join () **: descarta quaisquer casos na tabela da esquerda que não tenham uma correspondência na tabela da direita. Se houver várias correspondências de casos da direita para um caso da esquerda, ele mantém apenas uma cópia do caso da esquerda.

Credit: Garrick Aden-Buie – @grrrck

anti_join(): descarta quaisquer casos na tabela da esquerda que tenham uma correspondência na tabela da direita.

Credit: Garrick Aden-Buie – @grrrck

Examplo

Eles usam os dados de exemplo da seção anterior

Um semi_join () é usado para encontrar a idade e o status do aluguel (informações na tabela general_info) para os proprietários de animais de estimação:

general_info %>% 
  semi_join(pet_info %>% filter(pet_owner == "yes"), 
            by = "person_id") 

Isso retorna uma tabela com 3 linhas. Uma vez que são mesas pequenas, você deve verificar isso manualmente. Observe também que não pressionei enter após %>% dentro de semi_join (). Este é um caso em que o deixamos na mesma linha para torná-lo mais legível.

Use um anti_join () para encontrar a idade e o status do aluguel (informações na tabela general_info) para pessoas que não são donos confirmados de animais de estimação (observe que isso inclui desconhecidos):

general_info %>% 
  anti_join(pet_info %>% filter(pet_owner == "yes"),
            by = "person_id")

Demo video

Agora assista ao vídeo abaixo que irá guiá-lo por alguns exemplos de codificação mais avançados (além de uma participação especial de minha filha, Hadley). Os arquivos R Markdown para download para acompanhar são encontrados abaixo do vídeo pivotante.

Sua vez!

Exercise 1: mutating join

Exercício 1: mutação de junção

Resuma os dados garden_harvest para encontrar a colheita total em libras para cada variedade de vegetais e, em seguida, tente adicionar a parcela da tabelagarden_planting. Isso não sairá perfeitamente. Qual é o problema? Como você pode consertar isso?

Exercício 2: mutação de junção

Gostaria de saber quanto dinheiro “economizei” com a jardinagem, para cada tipo de vegetal. Descreva como eu poderia usar os conjuntos de dados garden_harvest egarden_spending, junto com dados de algum lugar como [este] (https://products.wholefoodsmarket.com/search?sort=relevance&store=10542) para responder a esta pergunta. Você pode responder isso em palavras, referenciando várias funções de junção. Você não precisa do código R, mas pode fornecer algum se for útil.

Exercício 3: junção de filtragem

Exclua as variedades de vegetais de colheita_do_jardim que estão nas parcelas M e H.

LS0tDQp0aXRsZTogIkF1bGEgNiINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KLS0tDQoNCiMjIFNldHVwDQoNCmBgYHtyIHNldHVwfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkNCmBgYA0KDQpgYGB7ciBsaWJyYXJpZXN9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAgICMgZm9yIGRhdGEgY2xlYW5pbmcgYW5kIHBsb3R0aW5nDQpsaWJyYXJ5KGx1YnJpZGF0ZSkgICAgICMgZm9yIGRhdGUgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKSMgZm9yIFBhbG1lciBwZW5ndWluIGRhdGENCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KDQpgYGB7ciBkYXRhfQ0KIyBMaXNhJ3MgZ2FyZGVuIGRhdGENCiMgUGFsbWVyIHBlbmd1aW5zDQpkYXRhKCJwZW5ndWlucyIpDQoNCiMgVVMgdHVpdGlvbiBieSBzdGF0ZSBkYXRhDQp1c19hdmdfdHVpdGlvbiA8LSByZWFkX2NzdigiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy85ZTBwYXJhZGN3dnV6bGwvdXNfYXZnX3R1aXRpb24uY3N2P2RsPTEiKSAlPiUgDQogICBtdXRhdGUoYWNyb3NzKHN0YXJ0c193aXRoKCIyMCIpLCBwYXJzZV9udW1iZXIpKQ0KYGBgDQoNCiMjIE1ldGFzIGRlIGFwcmVuZGl6YWdlbQ0KDQpBcMOzcyBlc3RlIHR1dG9yaWFsLCB2b2PDqiBkZXZlIHNlciBjYXBheiBkZSBmYXplciBvIHNlZ3VpbnRlOg0KDQoqIFVzZSBgcGl2b3RfbG9uZ2VyKClgIGUgYHBpdm90X3dpZGVyKClgIHBhcmEgYWx0ZXJhciBhIGZvcm1hIGNvbW8gb3MgZGFkb3Mgc8OjbyBkaXNwb3N0b3MuDQoqIEp1bnRlIHRhYmVsYXMgZGUgZGFkb3MgdXNhbmRvIGFzIGZ1bsOnw7VlcyBkZSBqdW7Dp8OjbyBkcGx5ciBlIGVudGVuZGEgYXMgZGlmZXJlbsOnYXMgZW50cmUgb3MgZGlmZXJlbnRlcyB0aXBvcyBkZSBqdW7Dp8O1ZXMuDQoqIFVzZSB2w6FyaWFzIGZ1bsOnw7VlcyBmb3JjYXRzLCBpbmNsdWluZG8gYXF1ZWxhcyBuw6NvIGFib3JkYWRhcyBubyB0dXRvcmlhbCwgcGFyYSBhbHRlcmFyIGEgb3JkZW0gb3Ugb3MgdmFsb3JlcyBkb3MgbsOtdmVpcyBkYXMgdmFyacOhdmVpcyBjYXRlZ8OzcmljYXMuDQoqIFVzZSBhcyBmdW7Dp8O1ZXMgc3RyaW5nciBhYm9yZGFkYXMgbmVzdGUgdHV0b3JpYWwgKGFsw6ltIGRlIGBzZXBhcmF0ZSgpYCkgZSBzYWliYSBvbmRlIGVuY29udHJhciBpbmZvcm1hw6fDtWVzIHNvYnJlIG91dHJhcyBmdW7Dp8O1ZXMgKnN0cmluZ3IqIChESUNBOiBhIGZvbGhhIGRlIGNoZWF0c2hlZXQgw6kgdW0gw7N0aW1vIGNvbWXDp28pLg0KDQojIyBBbHRlcmFyIG8gbGF5b3V0IGRlIGRhZG9zIGNvbSBmdW7Dp8O1ZXMgZGluw6JtaWNhcw0KDQpFc3RhIHBhcnRlIGRvIHR1dG9yaWFsIGlyw6EgYXByZXNlbnTDoS1sbyBhIGR1YXMgZnVuw6fDtWVzOiBgcGl2b3RfbG9uZ2VyICgpYCBlIGBwaXZvdF93aWRlciAoKWAuIEVzc2FzIGZ1bsOnw7VlcyBzw6NvIHVzYWRhcyBwYXJhIGFsdGVyYXIgYSBmb3JtYSBjb21vIG9zIGRhZG9zIHPDo28gZGlzcG9zdG9zLiBPIEdJRiBhYmFpeG8gaWx1c3RyYSBvIHF1ZSBhcyBmdW7Dp8O1ZXMgZmF6ZW0uIEV1IG8gZW5jb3Jham8gYSByZXZpc2l0YXIgZXN0YSBpbHVzdHJhw6fDo28gZGVwb2lzIGRlIGxlciBhcyBkZXNjcmnDp8O1ZXMgbWFpcyBkZXRhbGhhZGFzLg0KDQo8Y2VudGVyPg0KDQohW0ltYWdlIGNyZWRpdDogTWFyYSBBdmVyaWNrICh0d2VldCBmcm9tIDIwMTktMTAtMDQpXShodHRwczovL3d3dy5kcm9wYm94LmNvbS9zL2E2bzc1emo0NDNiMnJ2My90aWR5ci1sb25nZXItd2lkZXItbW9kaWZpZWQuZ2lmP2RsPTEpDQoNCjwvY2VudGVyPg0KDQojIyMgYHBpdm90X2xvbmdlcigpYA0KDQoqKiBgcGl2b3RfbG9uZ2VyICgpYCAqKjogdG9ybmEgbyBjb25qdW50byBkZSBkYWRvcyBtYWlzIGxvbmdvLCByZWR1emluZG8gbyBuw7ptZXJvIGRlIGNvbHVuYXMgZSBhdW1lbnRhbmRvIG8gbsO6bWVybyBkZSBsaW5oYXMuIEZyZXHDvGVudGVtZW50ZSB1c2FkbyBxdWFuZG8gb3Mgbm9tZXMgZGFzIGNvbHVuYXMgZGV2ZW0gc2VyIHZhbG9yZXMgZGUgdW1hIHZhcmnDoXZlbC4NCg0KT3MgZGFkb3MgYWJhaXhvIG1vc3RyYW0gb3MgY3VzdG9zIG3DqWRpb3MgZGFzIG1lbnNhbGlkYWRlcyB1bml2ZXJzaXTDoXJpYXMgbm9zIEVVQSBwb3IgZXN0YWRvLiBPYnNlcnZlIHF1ZSBvcyBhbm9zIHPDo28gbm9tZXMgZGUgY29sdW5hcy4NCg0KYGBge3IsIGVjaG89RkFMU0V9DQp1c19hdmdfdHVpdGlvbiANCmBgYA0KDQpBZ29yYSwgZ29zdGFyw61hbW9zIGRlIG11ZGFyIGlzc28gcGFyYSBxdWUgaGFqYSB1bWEgdmFyacOhdmVsIGNoYW1hZGEgYGFub2AgcXVlIGluZGljYXJpYSBvIGFubyBlIG9zIHZhbG9yZXMgZGEgYXVsYSBzZXJpYW0gdW1hIHZhcmnDoXZlbCDDum5pY2EgYW8gaW52w6lzIGRlIGVzcGFsaGFyIHBvciB2w6FyaWFzIHZhcmnDoXZlaXMgLWAgcGl2b3RfbG9uZ2VyICgpIGBwYXJhIG8gcmVzZ2F0ZSENCg0KTyBjw7NkaWdvIGdlbsOpcmljbyBwYXJhIGBwaXZvdF9sb25nZXIgKClgIMOpIG1vc3RyYWRvIGFxdWk6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KZGF0YSAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gX19fX19fX19fX18sDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJuYW1lX29mX2NvbHNfdmFyaWFibGUiLA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIm5hbWVfb2ZfdmFsdWVzX3ZhcmlhYmxlIikNCmBgYA0KDQpWYW1vcyB0ZW50YXIgZXNzZSBjb20gb3MgZGFkb3MgKnVzX2F2Z190dWl0aW9uKjoNCg0KYGBge3IgcGl2b3QtbG9uZ2VyLWV4MX0NCnVzX2F2Z190dWl0aW9uICU+JSANCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiMjAiKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInllYXIiLA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImF2Z190dWl0aW9uIikNCmBgYA0KDQpBZ29yYSBow6EgdW1hIGxpbmhhIHBhcmEgY2FkYSBjb21iaW5hw6fDo28gw7puaWNhIGRlIGVzdGFkbyBlIGFubyBlIGBhbm9gIGVgIGF2Z190dWl0aW9uYCBzw6NvIHZhcmnDoXZlaXMuIEVzdGUgY29uanVudG8gZGUgZGFkb3MgdGVtIG1haXMgbGluaGFzIGUgbWVub3MgY29sdW5hcyBkbyBxdWUgbyBjb25qdW50byBkZSBkYWRvcyBvcmlnaW5hbC4NCg0KVmFtb3MgZXhhbWluYXIgY2FkYSBhcmd1bWVudG8gZGEgZnVuw6fDo28gY29tIG1haXMgZGV0YWxoZXMuDQoNCk8gYXJndW1lbnRvIGBjb2xzYCBpbmRpY2EgcXVhaXMgY29sdW5hcyBkZXZlbSBzZXIgZGluYW1pemFkYXMgcGFyYSBxdWUgZXNzZXMgbm9tZXMgZGUgY29sdW5hIHNlIHRvcm5lbSB2YWxvcmVzIGRlIHVtYSBub3ZhIHZhcmnDoXZlbC4gVm9jw6ogcG9kZSBmYXplciB1bWEgbGlzdGEgZGUgbm9tZXMgZGUgY29sdW5hcyBvdSB1c2FyIGZ1bsOnw7VlcyBhdXhpbGlhcmVzIHBhcmEgc2VsZWNpb25hciBjb2x1bmFzICh2ZWphIGEgZnVuw6fDo28gYHNlbGVjdCAoKWAgbm8gY2hlYXRzaGVldCBgZHBseXJgIHBhcmEgbWFpcyBkZXRhbGhlcyBvdSBwcm9jdXJlIHBvcmAgdGlkeS1zZWxlY3RgIG5hIGFiYSBIZWxwKS4gRXUgdXNlaSBhIGZ1bsOnw6NvIGF1eGlsaWFyIGBzdGFydHNfd2l0aCAoKWAgbmVzdGUgZXhlbXBsby4NCg0KTyBhcmd1bWVudG8gYG5hbWVzX3RvYCDDqSBjb21vIHZvY8OqIGRlc2VqYSBub21lYXIgYSBub3ZhIHZhcmnDoXZlbCBvbmRlIG9zIG5vbWVzIGRhcyBjb2x1bmFzIHNlcsOjbyBhcm1hemVuYWRvcy4gSXNzbyBwcmVjaXNhIGVzdGFyIGVudHJlIGFzcGFzLg0KDQpUaGUgYHZhbHVlc190b2AgYXJndW1lbnQgaXMgd2hhdCB5b3Ugd2FudCB0byBuYW1lIHRoZSBuZXcgdmFyaWFibGUgd2hlcmUgdGhlIHZhbHVlcyB0aGF0IHVzZWQgdG8gYmUgc3ByZWFkIGFjcm9zcyBtdWx0aXBsZSBjb2x1bW5zIHdpbGwgbm93IGJlIHN0b3JlZCBpbiBvbmUgdmFyaWFibGUuICANCg0KIyMjIGBwaXZvdF93aWRlcigpYA0KDQpgcGl2b3Rfd2lkZXIgKClgOiB0b3JuYSBvIGNvbmp1bnRvIGRlIGRhZG9zIG1haXMgYW1wbG8sIHJlZHV6aW5kbyBvIG7Dum1lcm8gZGUgbGluaGFzIGUgYXVtZW50YW5kbyBvIG7Dum1lcm8gZGUgY29sdW5hcy4gRnJlccO8ZW50ZW1lbnRlIHVzYWRvIHF1YW5kbyBhcyBvYnNlcnZhw6fDtWVzIHPDo28gZGlzdHJpYnXDrWRhcyBlbSB2w6FyaWFzIGxpbmhhcyBlIG9zIHZhbG9yZXMgZGUgdW1hIHZhcmnDoXZlbCBkZXZlbSBzZXIgcmVhbG1lbnRlIHN1YXMgcHLDs3ByaWFzIHZhcmnDoXZlaXMuDQoNCkFxdWkgZXN0w6EgdW0gZXhlbXBsbyBlbSBxdWUgb3MgdmFsb3JlcyBkYSB0ZXJjZWlyYSBjb2x1bmEgKFRheGEgZGUgY3Jlc2NpbWVudG8gYW51YWwgZGEgcG9wdWxhw6fDo28gKHBvcmNlbnRhZ2VtKSwgVGF4YSBkZSBmZWN1bmRpZGFkZSB0b3RhbCAoZmlsaG9zIHBvciBtdWxoZXIpLCBldGMuKSBkZXZlbSBzZXIgY2FkYSB1bSBzdWEgcHLDs3ByaWEgdmFyacOhdmVsLg0KDQpBZ29yYSwgdmFtb3MgdmVyIHVtIGV4ZW1wbG8gc2VtZWxoYW50ZS4gRXUgY3JpZWkgdW0gbm92byBkYXRhc2V0IGNoYW1hZG8gYHBlbmd1aW5zX2Zha2VgIHF1ZSDDqSB1bWEgcmVvcmdhbml6YcOnw6NvIGRvcyBkYWRvcyBgcGVuZ3VpbnNgLg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCnBlbmd1aW5zX2Zha2UgPC0gcGVuZ3VpbnMgJT4lIA0KICBtdXRhdGUob2JzID0gcm93X251bWJlcigpKSAlPiUgIyBhIGZ1bmNhbyByb3dfbnVtYmVyKCkgdmFpIGdlcmFyIG9zIG51bWVyb3MgZGEgbGluaGEgZGEgb2JzZXJ2YcOnw6NvDQogIHBpdm90X2xvbmdlcihjb2xzID0gYmlsbF9sZW5ndGhfbW06Ym9keV9tYXNzX2csICMgbyAiOiIgaW5kaWNhIHF1ZSBxdWVyZW1vcyBvIGludGVydmFsbyBlbnRyZSBhcyB2YXJpYXZlaXMgDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJtZWFzdXJlbWVudCIsDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKQ0KYGBgDQoNCmBgYHtyfQ0KcGVuZ3VpbnNfZmFrZQ0KYGBgDQpFbSBgcGVuZ3VpbnNfZmFrZWAsIGEgY29sdW5hIGNoYW1hZGFgIG1lZGnDp8Ojb2AgcG9zc3VpIHRvZG9zIG9zIG5vbWVzIGRhcyBtZWRpw6fDtWVzLiBHb3N0YXLDrWFtb3MgZGUgZGV2b2x2w6otbG9zIGFvcyBub21lcyBkYXMgY29sdW5hcyBwYXJhIHF1ZSBoYWphIG5vdmFtZW50ZSBhcGVuYXMgdW1hIGxpbmhhIHBhcmEgY2FkYSBwaW5ndWltLiBVc2FyZW1vcyBgcGl2b3Rfd2lkZXIgKClgIHBhcmEgZmF6ZXIgaXNzbyEgT2JzZXJ2ZSBxdWUgdGFtYsOpbSBow6EgdW1hIGNvbHVuYSBjaGFtYWRhIGBvYnNgIHF1ZSBpZGVudGlmaWNhIGNhZGEgb2JzZXJ2YcOnw6NvIMO6bmljYSBkb3MgZGFkb3Mgb3JpZ2luYWlzIC0gaXNzbyDDqSBtdWl0byBpbXBvcnRhbnRlIQ0KDQpPIGPDs2RpZ28gZ2Vuw6lyaWNvIHBhcmEgYHBpdm90X3dpZGVyICgpYCDDqSBtb3N0cmFkbyBhcXVpOg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmRhdGEgJT4lIA0KICBwaXZvdF93aWRlcihpZF9jb2xzID0gX19fX19fX19fX18sDQogICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSB2YXJpYWJsZV93aXRoX25hbWVzLA0KICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHZhcmlhYmxlX3dpdGhfdmFsdWVzKQ0KYGBgDQoNClZhbW9zIGZhemVyIGlzc28gY29tIG9zIGRhZG9zIGBwZW5ndWluc19mYWtlYDoNCg0KYGBge3IgcGl2b3Qtd2lkZXItZXgxfQ0KcGVuZ3VpbnNfZmFrZSAlPiUgDQogIHBpdm90X3dpZGVyKGlkX2NvbHMgPSBzcGVjaWVzOm9icywNCiAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9IG1lYXN1cmVtZW50LA0KICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHZhbHVlKQ0KYGBgDQoNCkFnb3JhLCBhcyBxdWF0cm8gdmFyacOhdmVpcyBkZSBtZWRpw6fDo28gdMOqbSwgY2FkYSB1bWEsIHN1YSBwcsOzcHJpYSBjb2x1bmEgbm92YW1lbnRlLiBFc3RlIGNvbmp1bnRvIGRlIGRhZG9zIHRlbSBtYWlzIGNvbHVuYXMgZSBtZW5vcyBsaW5oYXMgZG8gcXVlIG8gY29uanVudG8gZGUgZGFkb3MgYHBlbmd1aW5zX2Zha2VgLg0KDQpWYW1vcyBleGFtaW5hciBjYWRhIGFyZ3VtZW50byBkYSBmdW7Dp8OjbyBjb20gbWFpcyBkZXRhbGhlcy4NCg0KTyBhcmd1bWVudG8gYGlkX2NvbHNgIMOpIG8gY29uanVudG8gZGUgY29sdW5hcyBxdWUgaWRlbnRpZmljYSBleGNsdXNpdmFtZW50ZSBjYWRhIG9ic2VydmHDp8Ojby4gUG9yIHBhZHLDo28sIHNlcsOjbyB0b2RhcyBhcyBjb2x1bmFzIHF1ZSBuw6NvIGVzdMOjbyBub3MgYXJndW1lbnRvcyBgbmFtZXNfZnJvbWAgZWAgdmFsdWVzX2Zyb21gLiBDb21vIGBjb2xzYCBkYSBmdW7Dp8Ojb2AgcGl2b3RfbG9uZ2VyICgpIGAsIHZvY8OqIHBvZGUgZmF6ZXIgdW1hIGxpc3RhIGRlIG5vbWVzIGRlIGNvbHVuYSBvdSB1c2FyIGZ1bsOnw7VlcyBhdXhpbGlhcmVzIHBhcmEgc2VsZWNpb25hciBjb2x1bmFzICh2ZWphIGEgZnVuw6fDo28gYHNlbGVjdCAoKWBuYSBmb2xoYSBkZSBjb21hbmRvYCBkcGx5cmAgcGFyYSBtYWlzIGRldGFsaGVzIG91IHByb2N1cmUgcG9yIGAgdGlkeS1zZWxlY3RgIG5hIGd1aWEgSGVscCkuDQoNCioqICEhQ1VJREFETyEhOiAqKiDDiSBmw6FjaWwgY29tZXRlciB1bSBlcnJvIG5vIGFyZ3VtZW50byBgaWRfY29sc2AuIFBvciBleGVtcGxvLCBubyBjw7NkaWdvIGFiYWl4bywgZXNxdWVjaSBkZSBpbmNsdWlyIGBvYnNgLiBPIHJlc3VsdGFkbyDDqSBhbGdvIGVzdHJhbmhvIGUgaW5lc3BlcmFkbyBjb20gYXBlbmFzIDM1IGxpbmhhcy4NCg0KYGBge3J9DQpwZW5ndWluc19mYWtlICU+JSANCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IHNwZWNpZXM6eWVhciwNCiAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9IG1lYXN1cmVtZW50LA0KICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHZhbHVlKQ0KYGBgDQoNCg0KTyBhcmd1bWVudG8gYG5hbWVzX2Zyb21gIMOpIGEgdmFyacOhdmVsIChvdSB2YXJpw6F2ZWlzKSBxdWUgY29udMOpbSBvcyB2YWxvcmVzIHF1ZSB2b2PDqiBkZXNlamEgdHJhbnNmb3JtYXIgZW0gc3VhcyBwcsOzcHJpYXMgY29sdW5hcy4gSXNzbyAqbsOjbyogZXN0w6EgZW50cmUgYXNwYXMuDQoNCk8gYXJndW1lbnRvIGB2YWx1ZXNfZnJvbWAgw6kgYSB2YXJpw6F2ZWwgKG91IHZhcmnDoXZlaXMpIHF1ZSBkZXZlbSBzZXIgb3MgdmFsb3JlcyBkYXMgbm92YXMgdmFyacOhdmVpcy4NCg0KIyMjIFZpZGVvIGRlIEFwcmVzZW50YcOnw6NvDQoNCkFnb3JhIHF1ZSB2b2PDqiBhcHJlbmRldSBvIGLDoXNpY28gc29icmUgcGl2b3RhciwgYXNzaXN0YSBhbyB2w61kZW8gYWJhaXhvIHF1ZSBvIGd1aWFyw6EgcG9yIGFsZ3VucyBleGVtcGxvcyBkZSBjb2RpZmljYcOnw6NvIGUgYmFpeGUgb3MgYXJxdWl2b3MgUiBNYXJrZG93biBwYXJhIGFjb21wYW5oYXIuIEVzdGUgw6kgbyBtZXNtbyBhcnF1aXZvIHF1ZSB2b2PDqiB1c2Fyw6EgcGFyYSBvcyBvdXRyb3MgdMOzcGljb3MuDQoNCjxpZnJhbWUgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvazNTWjhrZWlidVEiIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYWNjZWxlcm9tZXRlcjsgYXV0b3BsYXk7IGVuY3J5cHRlZC1tZWRpYTsgZ3lyb3Njb3BlOyBwaWN0dXJlLWluLXBpY3R1cmUiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4NCg0KDQojIyMgUmVzb3VyY2VzDQoNCiogW1NsaWRlc10oaHR0cHM6Ly9zcGVha2VyZGVjay5jb20veXV0YW5uaWhpbGF0aW9uL2EtZ3JhcGhpY2FsLWludHJvZHVjdGlvbi10by10aWR5cnMtcGl2b3Qtc3RhcikgZnJvbSBIaXJvYWtpIFl1dGFuaSAgDQoqIFtSNERTIENoYXB0ZXIgMTIuM10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90aWR5LWRhdGEuaHRtbCkNCg0KIyMjIFN1YSB2ZXohDQoNCiMjIyMgRXhlcmNpY2lvIDE6IGBwaXZvdF93aWRlcigpYA0KDQpSZXN1bWEgb3MgZGFkb3MgYGNvbGhlaXRhX2RvX2phcmRpbWAgcGFyYSBlbmNvbnRyYXIgbyBwZXNvIHRvdGFsIGRhIGNvbGhlaXRhIGVtIGxpYnJhcyBwYXJhIGNhZGEgdmVnZXRhbCBlIGRpYSBkYSBzZW1hbmEuIEV4aWJhIG9zIHJlc3VsdGFkb3MgZGUgZm9ybWEgcXVlIG9zIHZlZ2V0YWlzIHNlamFtIGxpbmhhcywgbWFzIG9zIGRpYXMgZGEgc2VtYW5hIHNlamFtIGNvbHVuYXMuDQoNCiMjIyMgRXhlcmNpc2UgMjogYHBpdm90X2xvbmdlcigpYA0KDQpVc2UgbyBjb25qdW50byBkZSBkYWRvcyBgYmlsbGJvYXJkYCAocHJvY3VyZSBuYSBhanVkYSBvdSBkaWdpdGVgPyBCaWxsYm9hcmRgIG5vIGNvbnNvbGUpLiBUZW0gY2xhc3NpZmljYcOnw7VlcyBkZSBtw7pzaWNhcyBwYXJhIGNhZGEgc2VtYW5hIHF1ZSBlbnRyYXJhbSBubyBUb3AgMTAwIGRhIEJpbGxib2FyZC4gQXMgc2VtYW5hcyBzw6NvIG5vbWVzIGRlIGNvbHVuYXMuIFVzZSBgcGl2b3RfbG9uZ2VyICgpYCBwYXJhIHRyYW5zZm9ybWFyIHNlbWFuYXMgZW0gdW1hIMO6bmljYSBjb2x1bmEgZSByZW1vdmEgbGluaGFzIGNvbSB2YWxvcmVzIGF1c2VudGVzIHBhcmEgY2xhc3NpZmljYcOnw6NvIChESUNBOiB1c2UgbyBhcmd1bWVudG8gYHZhbHVlc19kcm9wX25hYCBlbWAgcGl2b3RfbG9uZ2VyICgpIGApLg0KDQpgYGB7cn0NCg0KYGBgDQoNCg0KIyMgSm9pbmluZyBkYXRhc2V0cw0KDQpBbyBhbmFsaXNhciBkYWRvcywgw6kgY29tdW0gYSBuZWNlc3NpZGFkZSBkZSBjb21iaW5hciBjb25qdW50b3MgZGUgZGFkb3MgcmVsYWNpb25hZG9zLiBPcyB2ZXJib3MgYGpvaW5gIG5vcyBkYXLDo28gdW1hIG1hbmVpcmEgZGUgZmF6ZXIgaXNzby4gUGFyYSB0b2RhcyBhcyBqdW7Dp8O1ZXMsIGRldmVtb3MgZXN0YWJlbGVjZXIgdW1hIGNvcnJlc3BvbmTDqm5jaWEgb3UgY29ycmVzcG9uZMOqbmNpYSBlbnRyZSBjYWRhIGNhc28gbmEgdGFiZWxhIGRhIGVzcXVlcmRhIGUgemVybyBvdSBtYWlzIGNhc29zIG5hIHRhYmVsYSBkYSBkaXJlaXRhLg0KDQpVbWEgY29ycmVzcG9uZMOqbmNpYSBlbnRyZSB1bSBjYXNvIG5hIHRhYmVsYSAqbGVmdCogZSB1bSBjYXNvIG5hIHRhYmVsYSAqcmlnaHQqIMOpIGZlaXRhIGNvbSBiYXNlIG5vcyB2YWxvcmVzIGVtIHBhcmVzIGRlIHZhcmnDoXZlaXMgY29ycmVzcG9uZGVudGVzLg0KDQoqICoqVm9jw6oqKiBlc3BlY2lmaWNhIHF1YWlzIHBhcmVzIHVzYXIuDQoqIFVtIHBhciDDqSB1bWEgdmFyacOhdmVsIGRhIHRhYmVsYSBkYSBlc3F1ZXJkYSBlIHVtYSB2YXJpw6F2ZWwgZGEgdGFiZWxhIGRhIGRpcmVpdGEgb3UgdW0gY29uanVudG8gZGUgdmFyacOhdmVpcyBkYSB0YWJlbGEgZGEgZXNxdWVyZGEgZSBkYSBkaXJlaXRhLg0KKiBPcyBjYXNvcyBkZXZlbSB0ZXIgdmFsb3JlcyAqZXhhdGFtZW50ZSBpZ3VhaXMqIG5vIHBhciBwYXJhIHF1ZSB1bWEgY29ycmVzcG9uZMOqbmNpYSBzZWphIGZlaXRhLg0KDQpRdWFuZG8ganVudGFtb3MgY29uanVudG9zIGRlIGRhZG9zLCBvIGZvcm1hdG8gZ2VyYWwgw6kNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpsZWZ0X2RhdGFzZXQgJT4lIA0KICA8Sk9JTj4ocmlnaHRfZGF0YXNldCwgDQogICAgICAgICBieT08SE9XIFRPIEpPSU4+KQ0KYGBgDQpvbmRlIGBsZWZ0X2RhdGFzZXRgIGVgIHJpZ2h0X2RhdGFzZXRgIHPDo28gZGF0YXNldHMsIGA8Sk9JTj5gIMOpIG8gdGlwbyBlc3BlY8OtZmljbyBkZSBqdW7Dp8OjbywgZSBgPEhPVyBUTyBKT0lOPmAgZMOhIGluZm9ybWHDp8O1ZXMgZGV0YWxoYWRhcyBzb2JyZSBjb21vIGZhesOqLWxvLg0KDQpPIGFyZ3VtZW50byBgYnlgIGluZm9ybWEgY29tbyBqdW50YXIgb3MgZG9pcyBjb25qdW50b3MgZGUgZGFkb3MsIGVzcGVjaWZpY2FtZW50ZSBjb20gcXVhaXMgdmFyacOhdmVpcyBlbGUgZGV2ZSBjb3JyZXNwb25kZXIuIFNlIGFzIHZhcmnDoXZlaXMgZXN0aXZlcmVtIG9zIG1lc21vcyBub21lcywgc8OzIHByZWNpc2Ftb3MgZXNjcmV2ZXIgbyBub21lIGRlc3NhIHZhcmnDoXZlbCwgZW50cmUgYXNwYXM6IGBieSA9IiB2YXJpYWJsZV9uYW1lICJgLg0KDQpTZSBhcyBkdWFzIHZhcmnDoXZlaXMgYSBzZXJlbSBjb3JyZXNwb25kaWRhcyB0w6ptIG5vbWVzIGRpZmVyZW50ZXMgbm9zIGRvaXMgY29uanVudG9zIGRlIGRhZG9zLCBwb2RlbW9zIGVzY3JldmVyIGBieSA9IGMgKCIgbm9tZTEgIj0iIG5vbWUyICIpYCwgb25kZSBgbm9tZTFgIMOpIGEgdmFyacOhdmVsIG5vIGNvbmp1bnRvIGRlIGRhZG9zIGVzcXVlcmRvIGEgc2VyIGNvcnJlc3BvbmRpZG8gYW9gIG5vbWUyIGB2YXJpw6F2ZWwgbm8gY29uanVudG8gZGUgZGFkb3MgY29ycmV0by4NCg0KVGFtYsOpbSBwb2RlbW9zIGNvcnJlc3BvbmRlciBlbSB2w6FyaWFzIHZhcmnDoXZlaXMgdXNhbmRvIGBieSA9IGMgKCIgbm9tZTEgIj0iIG5vbWUyICIsIiBub21lMWEgIj0iIG5vbWUyYSAiKWAsIG9uZGUgb3Mgbm9tZXMgw6AgZXNxdWVyZGEgZG9zIGlndWFpcyBzw6NvIHZhcmnDoXZlaXMgZG8gY29uanVudG8gZGUgZGFkb3MgZXNxdWVyZG8gZSBhcXVlbGVzIG5vIMOgIGRpcmVpdGEgZG9zIGlndWFpcyBzw6NvIGRvIGNvbmp1bnRvIGRlIGRhZG9zIGNlcnRvLg0KDQpTZSBgYnkgPWAgZm9yIG9taXRpZG8gZGUgdW1hIGp1bsOnw6NvLCBlbnTDo28gYFJgIHJlYWxpemFyw6EgdW1hICoganVuw6fDo28gbmF0dXJhbCAqLCBxdWUgY29tYmluYSBvcyBkb2lzIGNvbmp1bnRvcyBkZSBkYWRvcyBwb3IgdG9kYXMgYXMgdmFyacOhdmVpcyBxdWUgZWxlcyB0w6ptIGVtIGNvbXVtLiDDiSB1bWEgYm9hIHByw6F0aWNhIHNlbXByZSBpbmNsdWlyIGBieSA9YC4NCg0KVmFtb3MgZGlzY3V0aXIgb3MgZGlmZXJlbnRlcyB0aXBvcyBkZSBqdW7Dp8O1ZXMuDQoNCiMjIyBNdXRhdGluZyBqb2lucw0KDQpBIHByaW1laXJhIGNsYXNzZSBkZSBqdW7Dp8O1ZXMgc8OjbyBqdW7Dp8O1ZXMgbXV0YW50ZXMsIHF1ZSBhZGljaW9uYW0gbm92YXMgdmFyacOhdmVpcyAoY29sdW5hcykgw6AgdGFiZWxhIGRlIGRhZG9zIGVzcXVlcmRhIGEgcGFydGlyIGRlIG9ic2VydmHDp8O1ZXMgY29ycmVzcG9uZGVudGVzIG5hIHRhYmVsYSBkaXJlaXRhLg0KDQpBIHByaW5jaXBhbCBkaWZlcmVuw6dhIG5hcyB0csOqcyBvcMOnw7VlcyBkZSBqdW7Dp8OjbyBtdXRhbnRlIG5lc3RhIGNsYXNzZSDDqSBjb21vIGVsYXMgcmVzcG9uZGVtIMOgcyBzZWd1aW50ZXMgcGVyZ3VudGFzOg0KDQoxLiBPIHF1ZSBhY29udGVjZSBxdWFuZG8gdW0gY2FzbyBuYSB0YWJlbGEgZGEgZGlyZWl0YSBuw6NvIHRlbSBjb3JyZXNwb25kw6puY2lhcyBuYSB0YWJlbGEgZGEgZXNxdWVyZGE/DQoyLiBPIHF1ZSBhY29udGVjZSBxdWFuZG8gdW0gY2FzbyBuYSB0YWJlbGEgZGEgZXNxdWVyZGEgbsOjbyB0ZW0gY29ycmVzcG9uZMOqbmNpYXMgbmEgdGFiZWxhIGRhIGRpcmVpdGE/DQoNClRyw6pzIGZ1bsOnw7VlcyBkZSBqdW7Dp8OjbyBtdXRhbnRlczoNCg0KKiogYGxlZnRfam9pbiAoKWAgKio6IGEgc2HDrWRhIHRlbSB0b2RvcyBvcyBjYXNvcyBkYSBlc3F1ZXJkYSwgaW5kZXBlbmRlbnRlbWVudGUgc2UgaG91dmVyIHVtYSBjb3JyZXNwb25kw6puY2lhIG5hIGRpcmVpdGEsIG1hcyBkZXNjYXJ0YSBxdWFpc3F1ZXIgY2Fzb3MgbmEgZGlyZWl0YSBxdWUgbsOjbyB0ZW5oYW0gdW1hIGNvcnJlc3BvbmTDqm5jaWEgbmEgZXNxdWVyZGEuIChIw6EgdGFtYsOpbSB1bWEgZnVuw6fDo28gKiogYHJpZ2h0X2pvaW4gKClgICoqIHF1ZSBmYXogbyBvcG9zdG8uKQ0KDQohW0NyZWRpdDogR2FycmljayBBZGVuLUJ1aWUg4oCTIEBncnJyY2tdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9nYWRlbmJ1aWUvdGlkeWV4cGxhaW4vbWFzdGVyL2ltYWdlcy9sZWZ0LWpvaW4uZ2lmKXt3aWR0aD0zMDB9DQoNCioqYGlubmVyX2pvaW4oKWAqKjogdGhlIG91dHB1dCBoYXMgb25seSB0aGUgY2FzZXMgZnJvbSB0aGUgbGVmdCB3aXRoIGEgbWF0Y2ggaW4gdGhlIHJpZ2h0Lg0KDQoNCiFbQ3JlZGl0OiBHYXJyaWNrIEFkZW4tQnVpZSDigJMgQGdycnJja10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dhZGVuYnVpZS90aWR5ZXhwbGFpbi9tYXN0ZXIvaW1hZ2VzL2lubmVyLWpvaW4uZ2lmKXt3aWR0aD0zMDB9DQoNCioqYGZ1bGxfam9pbigpYCoqOmEgc2HDrWRhIHRlbSB0b2RvcyBvcyBjYXNvcyBkYSBlc3F1ZXJkYSBlIGRhIGRpcmVpdGEuIElzc28gw6kgbWVub3MgY29tdW0gZG8gcXVlIG9zIGRvaXMgcHJpbWVpcm9zIG9wZXJhZG9yZXMgZGUgYXNzb2NpYcOnw6NvLg0KDQoNCiFbQ3JlZGl0OiBHYXJyaWNrIEFkZW4tQnVpZSDigJMgQGdycnJja10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dhZGVuYnVpZS90aWR5ZXhwbGFpbi9tYXN0ZXIvaW1hZ2VzL2Z1bGwtam9pbi5naWYpe3dpZHRoPTMwMH0NCg0KUXVhbmRvIGjDoSB2w6FyaWFzIGNvcnJlc3BvbmTDqm5jaWFzIG5hIHRhYmVsYSBkYSBkaXJlaXRhIHBhcmEgdW0gY2FzbyBlc3BlY8OtZmljbyBuYSB0YWJlbGEgZGEgZXNxdWVyZGEsIHRvZG9zIG9zIHRyw6pzIGRlc3NlcyBvcGVyYWRvcmVzIGRlIGp1bsOnw6NvIG11dGFudGUgcHJvZHV6ZW0gdW0gY2FzbyBzZXBhcmFkbyBuYSBub3ZhIHRhYmVsYSBwYXJhIGNhZGEgdW1hIGRhcyBjb3JyZXNwb25kw6puY2lhcyBkYSBkaXJlaXRhLg0KDQojIyMjIEV4YW1wbGVzDQoNClByaW1laXJvLCBjcmllIGRvaXMgcGVxdWVub3MgY29uanVudG9zIGRlIGRhZG9zOg0KDQpgYGB7cn0NCmdlbmVyYWxfaW5mbyA8LSB0aWJibGUoDQogIHBlcnNvbl9pZCA9IGMoMSwgMiwgMywgNCwgNSwgNiwgNywgOCwgOSwgMTApLA0KICBhZ2UgPSBjKDM0LCA1NCwgNjcsIDkyLCAyMSwgMzIsIDE4LCA0NSwgMzQsIDU1KSwNCiAgcmVudF9vcl9vd24gPSBjKCJyZW50IiwgInJlbnQiLCAib3duIiwgInJlbnQiLCAicmVudCIsICJvd24iLCAicmVudCIsICJvd24iLCAib3duIiwgIm93biIpDQopDQoNCmdlbmVyYWxfaW5mbw0KDQpwZXRfaW5mbyA8LSB0aWJibGUoDQogIHBlcnNvbl9pZCA9IGMoMiwzLDUsNyw4LDEwLDExLDEyLDEzLDE0LDE1KSwNCiAgcGV0X293bmVyID0gYygieWVzIiwgIm5vIiwgIm5vIiwgInllcyIsICJ5ZXMiLCAibm8iLCAibm8iLCAibm8iLCAieWVzIiwgIm5vIiwgIm5vIikNCikNCg0KcGV0X2luZm8NCmBgYA0KDQoxLiBDb21lY2UgY29tIGBnZW5lcmFsX2luZm9gIGUgYGxlZnRfam9pbigpYCBvIGBwZXRfaW5mb2AgYnkgYHBlcnNvbl9pZGA6DQoNCmBgYHtyfQ0KZ2VuZXJhbF9pbmZvICU+JSANCiAgbGVmdF9qb2luKHBldF9pbmZvLCANCiAgICAgICAgICAgIGJ5ID0gInBlcnNvbl9pZCIpDQpgYGANCg0KQSB0YWJlbGEgcmVzdWx0YW50ZSB0ZW0gMTAgbGluaGFzIGRlIGRhZG9zIC0gYXMgMTAgb2JzZXJ2YcOnw7VlcyBkZSBgZ2VuZXJhbF9pbmZvYC4gRXhpc3RlbSB2YWxvcmVzIGF1c2VudGVzIHBhcmEgYHBldF9vd25lcmAgcGFyYWAgcGVyc29uX2lkYCBxdWUgZXN0YXZhbSBuYSB0YWJlbGEgYGdlbmVyYWxfaW5mb2AgZSBuw6NvIG5hIHRhYmVsYWAgcGV0X2luZm9gLg0KDQoqKiA/Pz8gKiogQ29tbyBvcyByZXN1bHRhZG9zIG11ZGFyaWFtIHNlIGByaWdodF9qb2luICgpYCBmb3NzZSB1c2FkbyBubyBjw7NkaWdvIGFjaW1hIGVtIHZleiBkZSBgbGVmdF9qb2luICgpYD8NCg0KMi4gQ29tZWNlIGNvbSBgZ2VuZXJhbF9pbmZvYCBlYCBpbm5lcl9qb2luICgpIGBvYCBwZXRfaW5mb2AgcG9yIGBwZXJzb25faWRgOg0KDQpgYGB7cn0NCmdlbmVyYWxfaW5mbyAlPiUgDQogIGlubmVyX2pvaW4ocGV0X2luZm8sIA0KICAgICAgICAgICAgIGJ5ID0gInBlcnNvbl9pZCIpDQpgYGANCg0KQSB0YWJlbGEgcmVzdWx0YW50ZSB0ZW0gYXBlbmFzIDYgbGluaGFzIGNvbSBhcyBvYnNlcnZhw6fDtWVzIHF1ZSBlc3TDo28gZW0gYGdlbmVyYWxfaW5mb2AgZWAgcGV0X2luZm9gLg0KDQozLiBDb21lY2UgY29tIGBnZW5lcmFsX2luZm9gIGVgIGZ1bGxfam9pbiAoKSBgb2AgcGV0X2luZm9gIHBvciBgcGVyc29uX2lkYDoNCg0KYGBge3J9DQpnZW5lcmFsX2luZm8gJT4lIA0KICBmdWxsX2pvaW4ocGV0X2luZm8sIA0KICAgICAgICAgICAgYnkgPSAicGVyc29uX2lkIikNCmBgYA0KDQpBIHRhYmVsYSByZXN1bHRhbnRlIHBvc3N1aSAxNSBsaW5oYXMuIEVzdMOjbyBmYWx0YW5kbyB2YWxvcmVzIHBhcmEgYHBldF9vd25lcmAgcGFyYWAgcGVyc29uX2lkYCBxdWUgZXN0YXZhbSBuYSB0YWJlbGEgYGdlbmVyYWxfaW5mb2AgZSBuw6NvIG5hIHRhYmVsYWAgcGV0X2luZm9gLCBlIGVzdMOjbyBmYWx0YW5kbyB2YWxvcmVzIHBhcmEgYGlkYWRlYCBlYCBhbHVndWVsYCBwYXJhIGBwZXJzb25faWRgIHF1ZSBlc3RhdmFtIG5hIHRhYmVsYSBgcGV0X2luZm9gIGUgbsOjbyBuYSB0YWJlbGFgIGdlbmVyYWxfaW5mb2AuDQoNCiMjIyBGaWx0ZXJpbmcgam9pbnMNCg0KQSBzZWd1bmRhIGNsYXNzZSBkZSBqdW7Dp8O1ZXMgc8OjbyBqdW7Dp8O1ZXMgZGUgZmlsdHJhZ2VtLCBxdWUgc2VsZWNpb25hbSBjYXNvcyBlc3BlY8OtZmljb3MgZGEgdGFiZWxhIGRhIGVzcXVlcmRhIGNvbSBiYXNlIGVtIHNlIGVsZXMgY29ycmVzcG9uZGVtIGEgdW1hIG9ic2VydmHDp8OjbyBuYSB0YWJlbGEgZGEgZGlyZWl0YS4NCg0KKiogYHNlbWlfam9pbiAoKWAgKio6IGRlc2NhcnRhIHF1YWlzcXVlciBjYXNvcyBuYSB0YWJlbGEgZGEgZXNxdWVyZGEgcXVlIG7Do28gdGVuaGFtIHVtYSBjb3JyZXNwb25kw6puY2lhIG5hIHRhYmVsYSBkYSBkaXJlaXRhLiBTZSBob3V2ZXIgdsOhcmlhcyBjb3JyZXNwb25kw6puY2lhcyBkZSBjYXNvcyBkYSBkaXJlaXRhIHBhcmEgdW0gY2FzbyBkYSBlc3F1ZXJkYSwgZWxlIG1hbnTDqW0gYXBlbmFzIHVtYSBjw7NwaWEgZG8gY2FzbyBkYSBlc3F1ZXJkYS4NCg0KDQohW0NyZWRpdDogR2FycmljayBBZGVuLUJ1aWUg4oCTIEBncnJyY2tdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9nYWRlbmJ1aWUvdGlkeWV4cGxhaW4vbWFzdGVyL2ltYWdlcy9zZW1pLWpvaW4uZ2lmKXt3aWR0aD0zMDB9DQoNCioqYGFudGlfam9pbigpYCoqOiBkZXNjYXJ0YSBxdWFpc3F1ZXIgY2Fzb3MgbmEgdGFiZWxhIGRhIGVzcXVlcmRhIHF1ZSB0ZW5oYW0gdW1hIGNvcnJlc3BvbmTDqm5jaWEgbmEgdGFiZWxhIGRhIGRpcmVpdGEuDQoNCg0KIVtDcmVkaXQ6IEdhcnJpY2sgQWRlbi1CdWllIOKAkyBAZ3JycmNrXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZ2FkZW5idWllL3RpZHlleHBsYWluL21hc3Rlci9pbWFnZXMvYW50aS1qb2luLmdpZil7d2lkdGg9MzAwfQ0KDQojIyMjIEV4YW1wbG8NCg0KRWxlcyB1c2FtIG9zIGRhZG9zIGRlIGV4ZW1wbG8gZGEgc2XDp8OjbyBhbnRlcmlvcg0KDQpVbSBgc2VtaV9qb2luICgpYCDDqSB1c2FkbyBwYXJhIGVuY29udHJhciBhIGlkYWRlIGUgbyBzdGF0dXMgZG8gYWx1Z3VlbCAoaW5mb3JtYcOnw7VlcyBuYSB0YWJlbGEgYGdlbmVyYWxfaW5mb2ApIHBhcmEgb3MgcHJvcHJpZXTDoXJpb3MgZGUgYW5pbWFpcyBkZSBlc3RpbWHDp8OjbzoNCg0KYGBge3J9DQpnZW5lcmFsX2luZm8gJT4lIA0KICBzZW1pX2pvaW4ocGV0X2luZm8gJT4lIGZpbHRlcihwZXRfb3duZXIgPT0gInllcyIpLCANCiAgICAgICAgICAgIGJ5ID0gInBlcnNvbl9pZCIpIA0KYGBgDQoNCklzc28gcmV0b3JuYSB1bWEgdGFiZWxhIGNvbSAzIGxpbmhhcy4gVW1hIHZleiBxdWUgc8OjbyBtZXNhcyBwZXF1ZW5hcywgdm9jw6ogZGV2ZSB2ZXJpZmljYXIgaXNzbyBtYW51YWxtZW50ZS4gT2JzZXJ2ZSB0YW1iw6ltIHF1ZSBuw6NvIHByZXNzaW9uZWkgZW50ZXIgYXDDs3MgYCU+JWAgZGVudHJvIGRlIGBzZW1pX2pvaW4gKClgLiBFc3RlIMOpIHVtIGNhc28gZW0gcXVlIG8gZGVpeGFtb3MgbmEgbWVzbWEgbGluaGEgcGFyYSB0b3Juw6EtbG8gbWFpcyBsZWfDrXZlbC4NCg0KVXNlIHVtIGBhbnRpX2pvaW4gKClgIHBhcmEgZW5jb250cmFyIGEgaWRhZGUgZSBvIHN0YXR1cyBkbyBhbHVndWVsIChpbmZvcm1hw6fDtWVzIG5hIHRhYmVsYSBgZ2VuZXJhbF9pbmZvYCkgcGFyYSBwZXNzb2FzIHF1ZSBuw6NvIHPDo28gZG9ub3MgY29uZmlybWFkb3MgZGUgYW5pbWFpcyBkZSBlc3RpbWHDp8OjbyAob2JzZXJ2ZSBxdWUgaXNzbyBpbmNsdWkgZGVzY29uaGVjaWRvcyk6DQoNCmBgYHtyfQ0KZ2VuZXJhbF9pbmZvICU+JSANCiAgYW50aV9qb2luKHBldF9pbmZvICU+JSBmaWx0ZXIocGV0X293bmVyID09ICJ5ZXMiKSwNCiAgICAgICAgICAgIGJ5ID0gInBlcnNvbl9pZCIpDQpgYGANCg0KIyMjIERlbW8gdmlkZW8NCg0KQWdvcmEgYXNzaXN0YSBhbyB2w61kZW8gYWJhaXhvIHF1ZSBpcsOhIGd1acOhLWxvIHBvciBhbGd1bnMgZXhlbXBsb3MgZGUgY29kaWZpY2HDp8OjbyBtYWlzIGF2YW7Dp2Fkb3MgKGFsw6ltIGRlIHVtYSBwYXJ0aWNpcGHDp8OjbyBlc3BlY2lhbCBkZSBtaW5oYSBmaWxoYSwgSGFkbGV5KS4gT3MgYXJxdWl2b3MgUiBNYXJrZG93biBwYXJhIGRvd25sb2FkIHBhcmEgYWNvbXBhbmhhciBzw6NvIGVuY29udHJhZG9zIGFiYWl4byBkbyB2w61kZW8gcGl2b3RhbnRlLg0KDQo8aWZyYW1lIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL01KREhSdHdaaG9NIiBmcmFtZWJvcmRlcj0iMCIgYWxsb3c9ImFjY2VsZXJvbWV0ZXI7IGF1dG9wbGF5OyBlbmNyeXB0ZWQtbWVkaWE7IGd5cm9zY29wZTsgcGljdHVyZS1pbi1waWN0dXJlIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+DQoNCiMjIyBSZWN1cnNvcw0KDQoqIFtBbmltYXRlZCBHSUZzXShodHRwczovL2dpdGh1Yi5jb20vZ2FkZW5idWllL3RpZHlleHBsYWluKSAgDQoqIFtSNERTIENoYXB0ZXIgMTNdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovcmVsYXRpb25hbC1kYXRhLmh0bWwpICANCiogW0pvaW4gQ2hlYXRzaGVldF0oaHR0cHM6Ly9zdGF0NTQ1LmNvbS9qb2luLWNoZWF0c2hlZXQuaHRtbCkgYnkgSmVubnkgQnJ5YW4NCg0KDQojIyMgU3VhIHZleiENCg0KIyMjIyBFeGVyY2lzZSAxOiBtdXRhdGluZyBqb2luDQoNCiMjIyMgRXhlcmPDrWNpbyAxOiBtdXRhw6fDo28gZGUganVuw6fDo28NCg0KUmVzdW1hIG9zIGRhZG9zIGBnYXJkZW5faGFydmVzdGAgcGFyYSBlbmNvbnRyYXIgYSBjb2xoZWl0YSB0b3RhbCBlbSBsaWJyYXMgcGFyYSBjYWRhIHZhcmllZGFkZSBkZSB2ZWdldGFpcyBlLCBlbSBzZWd1aWRhLCB0ZW50ZSBhZGljaW9uYXIgYSBwYXJjZWxhIGRhIHRhYmVsYWAgZ2FyZGVuX3BsYW50aW5nYC4gSXNzbyBuw6NvIHNhaXLDoSBwZXJmZWl0YW1lbnRlLiBRdWFsIMOpIG8gcHJvYmxlbWE/IENvbW8gdm9jw6ogcG9kZSBjb25zZXJ0YXIgaXNzbz8NCg0KYGBge3J9DQoNCmBgYA0KIyMjIyBFeGVyY8OtY2lvIDI6IG11dGHDp8OjbyBkZSBqdW7Dp8Ojbw0KDQpHb3N0YXJpYSBkZSBzYWJlciBxdWFudG8gZGluaGVpcm8gImVjb25vbWl6ZWkiIGNvbSBhIGphcmRpbmFnZW0sIHBhcmEgY2FkYSB0aXBvIGRlIHZlZ2V0YWwuIERlc2NyZXZhIGNvbW8gZXUgcG9kZXJpYSB1c2FyIG9zIGNvbmp1bnRvcyBkZSBkYWRvcyBgZ2FyZGVuX2hhcnZlc3RgIGVgIGdhcmRlbl9zcGVuZGluZ2AsIGp1bnRvIGNvbSBkYWRvcyBkZSBhbGd1bSBsdWdhciBjb21vIFtlc3RlXSAoaHR0cHM6Ly9wcm9kdWN0cy53aG9sZWZvb2RzbWFya2V0LmNvbS9zZWFyY2g/c29ydD1yZWxldmFuY2Umc3RvcmU9MTA1NDIpIHBhcmEgcmVzcG9uZGVyIGEgZXN0YSBwZXJndW50YS4gVm9jw6ogcG9kZSByZXNwb25kZXIgaXNzbyBlbSBwYWxhdnJhcywgcmVmZXJlbmNpYW5kbyB2w6FyaWFzIGZ1bsOnw7VlcyBkZSBqdW7Dp8Ojby4gVm9jw6ogbsOjbyBwcmVjaXNhIGRvIGPDs2RpZ28gUiwgbWFzIHBvZGUgZm9ybmVjZXIgYWxndW0gc2UgZm9yIMO6dGlsLg0KDQojIyMjIEV4ZXJjw61jaW8gMzoganVuw6fDo28gZGUgZmlsdHJhZ2VtDQoNCkV4Y2x1YSBhcyB2YXJpZWRhZGVzIGRlIHZlZ2V0YWlzIGRlIGBjb2xoZWl0YV9kb19qYXJkaW1gIHF1ZSBlc3TDo28gbmFzIHBhcmNlbGFzIE0gZSBILg0KDQpgYGB7cn0NCg0KYGBgDQo=